#include "U2TextureManager.h"

#include "U2Exception.h"
#include "U2PixelUtil.h"



U2EG_NAMESPACE_USING


//-----------------------------------------------------------------------
template<> U2TextureManager* U2Singleton<U2TextureManager>::s_pSingleton = 0;
U2TextureManager* U2TextureManager::getSingletonPtr(void)
{
    return s_pSingleton;
}
U2TextureManager& U2TextureManager::getSingleton(void)
{  
    assert( s_pSingleton );
    return ( *s_pSingleton );  
}
//-----------------------------------------------------------------------
U2TextureManager::U2TextureManager(void)
     : mPreferredIntegerBitDepth(0)
     , mPreferredFloatBitDepth(0)
     , mDefaultNumMipmaps(MIP_UNLIMITED)
{
    mResourceType = "Texture";
    mLoadOrder = 75.0f;

    // Subclasses should register (when this is fully constructed)
}
//-----------------------------------------------------------------------
U2TextureManager::~U2TextureManager()
{
    // subclasses should unregister with resource group manager

}
//-----------------------------------------------------------------------
U2TextureManager::ResourceCreateOrRetrieveResult U2TextureManager::createOrRetrieve(
        const U2String &name, const U2String& group, bool isManual, U2ManualResourceLoader* loader,
        const NameValuePairList* createParams, TextureType texType, int numMipmaps, u2real gamma,
        bool isAlpha, PixelFormat desiredFormat, bool hwGamma)
{
	ResourceCreateOrRetrieveResult res =
        U2ResourceManager::createOrRetrieve(name, group, isManual, loader, createParams);
	// Was it created?
	if(res.second)
    {
        U2TexturePtr tex = res.first;
        tex->setTextureType(texType);
        tex->setNumMipmaps((numMipmaps == MIP_DEFAULT)? mDefaultNumMipmaps :
			static_cast<size_t>(numMipmaps));
        tex->setGamma(gamma);
        tex->setTreatLuminanceAsAlpha(isAlpha);
        tex->setFormat(desiredFormat);
		tex->setHardwareGammaEnabled(hwGamma);
    }
    return res;
}
//-----------------------------------------------------------------------
U2TexturePtr U2TextureManager::prepare(const U2String &name, const U2String& group, TextureType texType,
                                   int numMipmaps, u2real gamma, bool isAlpha,
                                   PixelFormat desiredFormat, bool hwGamma)
{
	ResourceCreateOrRetrieveResult res =
        createOrRetrieve(name,group,false,0,0,texType,numMipmaps,gamma,isAlpha,desiredFormat,hwGamma);
    U2TexturePtr tex = res.first;
	tex->prepare();
    return tex;
}
//-----------------------------------------------------------------------
U2TexturePtr U2TextureManager::load(const U2String &name, const U2String& group, TextureType texType,
                                int numMipmaps, u2real gamma, bool isAlpha, PixelFormat desiredFormat,
                                bool hwGamma)
{
	ResourceCreateOrRetrieveResult res =
        createOrRetrieve(name,group,false,0,0,texType,numMipmaps,gamma,isAlpha,desiredFormat,hwGamma);
    U2TexturePtr tex = res.first;
	tex->load();
    return tex;
}

//-----------------------------------------------------------------------
U2TexturePtr U2TextureManager::loadImage( const U2String &name, const U2String& group,
    const U2Image &img, TextureType texType, int numMipmaps, u2real gamma, bool isAlpha, 
	PixelFormat desiredFormat, bool hwGamma)
{
    U2TexturePtr tex = create(name, group, true);

    tex->setTextureType(texType);
    tex->setNumMipmaps((numMipmaps == MIP_DEFAULT)? mDefaultNumMipmaps :
		static_cast<size_t>(numMipmaps));
    tex->setGamma(gamma);
    tex->setTreatLuminanceAsAlpha(isAlpha);
    tex->setFormat(desiredFormat);
	tex->setHardwareGammaEnabled(hwGamma);
    tex->loadImage(img);

    return tex;
}
//-----------------------------------------------------------------------
U2TexturePtr U2TextureManager::loadRawData(const U2String &name, const U2String& group,
    U2DataStreamPtr& stream, u2ushort uWidth, u2ushort uHeight, 
    PixelFormat format, TextureType texType, 
    int numMipmaps, u2real gamma, bool hwGamma)
{
    U2TexturePtr tex = create(name, group, true);

    tex->setTextureType(texType);
    tex->setNumMipmaps((numMipmaps == MIP_DEFAULT)? mDefaultNumMipmaps :
		static_cast<size_t>(numMipmaps));
    tex->setGamma(gamma);
	tex->setHardwareGammaEnabled(hwGamma);
	tex->loadRawData(stream, uWidth, uHeight, format);
	
    return tex;
}
//-----------------------------------------------------------------------
U2TexturePtr U2TextureManager::createManual(const U2String & name, const U2String& group,
    TextureType texType, u2uint width, u2uint height, u2uint depth, int numMipmaps,
    PixelFormat format, int usage, U2ManualResourceLoader* loader, bool hwGamma, 
	u2uint fsaa, const U2String& fsaaHint)
{
    U2TexturePtr ret = create(name, group, true, loader);
    ret->setTextureType(texType);
    ret->setWidth(width);
    ret->setHeight(height);
	ret->setDepth(depth);
    ret->setNumMipmaps((numMipmaps == MIP_DEFAULT)? mDefaultNumMipmaps :
		static_cast<size_t>(numMipmaps));
    ret->setFormat(format);
    ret->setUsage(usage);
	ret->setHardwareGammaEnabled(hwGamma);
	ret->setFSAA(fsaa, fsaaHint);
	ret->createInternalResources();
	return ret;
}
//-----------------------------------------------------------------------
void U2TextureManager::setPreferredIntegerBitDepth(u2ushort bits, bool reloadTextures)
{
    mPreferredIntegerBitDepth = bits;

    if (reloadTextures)
    {
        // Iterate through all textures
        for (ResourceMap::iterator it = mResources.begin(); it != mResources.end(); ++it)
        {
            U2Texture* texture = static_cast<U2Texture*>(it->second.get());
            // Reload loaded and reloadable texture only
            if (texture->isLoaded() && texture->isReloadable())
            {
                texture->unload();
                texture->setDesiredIntegerBitDepth(bits);
                texture->load();
            }
            else
            {
                texture->setDesiredIntegerBitDepth(bits);
            }
        }
    }
}
//-----------------------------------------------------------------------
u2ushort U2TextureManager::getPreferredIntegerBitDepth(void) const
{
    return mPreferredIntegerBitDepth;
}
//-----------------------------------------------------------------------
void U2TextureManager::setPreferredFloatBitDepth(u2ushort bits, bool reloadTextures)
{
    mPreferredFloatBitDepth = bits;

    if (reloadTextures)
    {
        // Iterate through all textures
        for (ResourceMap::iterator it = mResources.begin(); it != mResources.end(); ++it)
        {
            U2Texture* texture = static_cast<U2Texture*>(it->second.get());
            // Reload loaded and reloadable texture only
            if (texture->isLoaded() && texture->isReloadable())
            {
                texture->unload();
                texture->setDesiredFloatBitDepth(bits);
                texture->load();
            }
            else
            {
                texture->setDesiredFloatBitDepth(bits);
            }
        }
    }
}
//-----------------------------------------------------------------------
u2ushort U2TextureManager::getPreferredFloatBitDepth(void) const
{
    return mPreferredFloatBitDepth;
}
//-----------------------------------------------------------------------
void U2TextureManager::setPreferredBitDepths(u2ushort integerBits, u2ushort floatBits, bool reloadTextures)
{
    mPreferredIntegerBitDepth = integerBits;
    mPreferredFloatBitDepth = floatBits;

    if (reloadTextures)
    {
        // Iterate through all textures
        for (ResourceMap::iterator it = mResources.begin(); it != mResources.end(); ++it)
        {
            U2Texture* texture = static_cast<U2Texture*>(it->second.get());
            // Reload loaded and reloadable texture only
            if (texture->isLoaded() && texture->isReloadable())
            {
                texture->unload();
                texture->setDesiredBitDepths(integerBits, floatBits);
                texture->load();
            }
            else
            {
                texture->setDesiredBitDepths(integerBits, floatBits);
            }
        }
    }
}
//-----------------------------------------------------------------------
void U2TextureManager::setDefaultNumMipmaps( size_t num )
{
    mDefaultNumMipmaps = num;
}
//-----------------------------------------------------------------------
bool U2TextureManager::isFormatSupported(TextureType ttype, PixelFormat format, int usage)
{
	return getNativeFormat(ttype, format, usage) == format;
}
//-----------------------------------------------------------------------
bool U2TextureManager::isEquivalentFormatSupported(TextureType ttype, PixelFormat format, int usage)
{
	PixelFormat supportedFormat = getNativeFormat(ttype, format, usage);

	// Assume that same or greater number of bits means quality not degraded
	return U2PixelUtil::getNumElemBits(supportedFormat) >= U2PixelUtil::getNumElemBits(format);
	
}

